import type { RequestOptions, Result, UploadFileParams } from "/#/axios";
import type { CreateAxiosOptions } from "./axiosTransform";
import { isFunction } from "/@/utils/is";
import { AxiosCanceler } from "./axiosCancel";
import { ContentTypeEnum } from "../enums";
import { cloneDeep } from "lodash-es";
import axios from "axios";
import type {
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  AxiosError
} from "axios";

/**
 * @description:  axios 模块
 */
export class VAxios {
  private axiosInstance: AxiosInstance;
  private readonly options: CreateAxiosOptions;
  constructor(options: CreateAxiosOptions) {
    this.options = options; // 全局的配置参数数据
    this.axiosInstance = axios.create(options); // 创建一个axios实例
    this.setInterceptors();
  }
  /**
   * 创建 axios 实例
   * @param config
   */
  private createAxios(config: CreateAxiosOptions): void {
    this.axiosInstance = axios.create(config);
  }
  /**
   *  获取 axios 实例
   */
  getAxios(): AxiosInstance {
    return this.axiosInstance;
  }
  /**
   * 重新配置 axios
   * @param config
   * @returns
   */
  configAxios(config: CreateAxiosOptions) {
    if (!this.axiosInstance) return;
    this.createAxios(config);
  }
  /**
   * 设置通用头
   * @param headers
   * @returns
   */
  setHeader(headers: any): void {
    if (!this.axiosInstance) return;
    Object.assign(this.axiosInstance.defaults.headers, headers);
  }
  /**
   * 获取拦截器转化
   */
  private getTransform() {
    const { transform } = this.options;
    return transform;
  }
  /**
   * 设置拦截器配置
   */
  private setInterceptors() {
    const transform = this.getTransform();
    if (!transform) return;
    const { requestInterceptors, responseInterceptors } = transform; // 解构处理拦截器的方法
    const axiosCanceler = new AxiosCanceler();
    // 请求拦截器配置处理
    this.axiosInstance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        axiosCanceler.addPending(config);
        if (requestInterceptors && isFunction(requestInterceptors)) {
          config = requestInterceptors(config, this.options);
        }
        return config;
      },
      (err) => {
        return Promise.reject(err);
      }
    );
    // 响应拦截器配置处理
    this.axiosInstance.interceptors.response.use(
      (res: AxiosResponse<any>) => {
        res && axiosCanceler.removePending(res.config);
        if (responseInterceptors && isFunction(responseInterceptors)) {
          res = responseInterceptors(res);
        }
        return res;
      },
      (error) => {
        return Promise.reject(error);
      }
    );
  }
  /**
   * 文件上传
   * @param config
   * @param params
   * @returns
   */
  uploadFile<T = any>(config: AxiosRequestConfig, params: UploadFileParams) {
    const formData = new window.FormData();
    const customFilename = params.name || "file";
    // 获取 formData 数据
    if (params.filename) {
      formData.append(customFilename, params.file, params.filename);
    } else {
      formData.append(customFilename, params.file);
    }
    // 其他数据
    if (params.data) {
      Object.keys(params.data).forEach((key) => {
        const value = params.data[key];
        if (Array.isArray(value)) {
          value.forEach((item) => {
            formData.append(`${key}[]`, item);
          });
          return;
        }
        formData.append(key, params.data[key]);
      });
    }
    return this.axiosInstance.request<T>({
      ...config,
      method: "POST",
      data: formData,
      headers: {
        "Content-type": ContentTypeEnum.FORM_DATA,
        ignoreCancelToken: true
      }
    });
  }
  // get 请求
  public get<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    return this.request({ ...config, method: "GET" }, options);
  }
  // post 请求
  public post<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    return this.request({ ...config, method: "POST" }, options);
  }
  // put 请求
  public put<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    return this.request({ ...config, method: "PUT" }, options);
  }
  // delete 请求
  public delete<T = any>(
    config: AxiosRequestConfig,
    options?: RequestOptions
  ): Promise<T> {
    return this.request({ ...config, method: "DELETE" }, options);
  }
  // 基本请求
  public request<T = any>(
    config: AxiosRequestConfig, // 基本请求配置项
    options?: RequestOptions // 其他额外请求参数
  ): Promise<T> {
    // 请求配置项
    let conf: CreateAxiosOptions = cloneDeep(config); // 深度克隆
    const transform = this.getTransform();
    const { requestOptions } = this.options;
    const opt: RequestOptions = Object.assign({}, requestOptions, options);
    const { beforeRequestHook, transformResponseHook, requestCatchHook } =
      transform || {};
    // 对配置项进行处理
    if (beforeRequestHook && isFunction(beforeRequestHook)) {
      conf = beforeRequestHook(conf, opt);
    }
    conf.requestOptions = opt;

    // console.log(conf, 4563);

    //! tips 此处可以对 conf 配置项进行更细处理
    return new Promise((resolve, reject) => {
      this.axiosInstance
        .request<any, AxiosResponse<Result>>(conf)
        .then((res: AxiosResponse<Result>) => {
          if (transformResponseHook && isFunction(transformResponseHook)) {
            try {
              const ret = transformResponseHook(res, opt);
              resolve(ret);
            } catch (err) {
              reject(err || new Error("request error!"));
            }
            return;
          }
          resolve(res as unknown as Promise<T>);
        })
        .catch((e: Error | AxiosError) => {
          if (requestCatchHook && isFunction(requestCatchHook)) {
            reject(requestCatchHook(e, opt));
            return;
          }
          if (axios.isAxiosError(e)) {
            // 在这里可以改写 axios 错误消息
          }
          reject(e);
        });
    });
  }
}
